package org.example.project.bookstore.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import io.jsonwebtoken.Claims;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.example.bean.Seckill;
import org.example.config.JwtConfig;
import org.example.mapper.SeckillMapper;
import org.example.project.bookstore.service.SeckillService;
import org.example.util.DateUtil;
import org.example.util.JsonResult;
import org.example.util.ResultTool;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.core.*;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.List;

/**
 * @Auther:Liu
 * @Testname:SeckillServiceImpl
 * @Date:2025/8/28 16:28
 */
@Service
@Slf4j
public class SeckillServiceImpl extends ServiceImpl<SeckillMapper, Seckill> implements SeckillService {
    @Resource
    private SeckillMapper seckillMapper;
    public static final String SECKILL_KEY = "seckill"; //所有秒杀物品信息
    public static final String SECKILL_KEY_PREFIX = "seckill_prefix_"; //秒杀物品的库存信息
    public static final String SECKILL_KEY_USER = "seckill_user_";  //秒杀成功用户的信息
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    @Cacheable(cacheNames = "loadSeckill")
    public JsonResult loadSeckill() {
        HashOperations<String, Object, Object> forHash = stringRedisTemplate.opsForHash();
        ValueOperations<String, String> forValue = stringRedisTemplate.opsForValue();
        List<Seckill> list = list();
        for (Seckill seckill : list) {
            forHash.put(SECKILL_KEY,seckill.getId(), JSONArray.toJSONString(seckill)); //将秒杀物品信息存入Redis
            forValue.set(SECKILL_KEY_PREFIX + seckill.getId(),seckill.getSeckillNumber()+""); //将秒杀物品数量存入Redis
        }
        return ResultTool.success(list);
    }

    @Override
    public JsonResult seckill(String seckillId, HttpServletRequest request) {
        HashOperations<String, Object, Object> forHash = stringRedisTemplate.opsForHash();
        ValueOperations<String, String> forValue = stringRedisTemplate.opsForValue();
        SetOperations<String, String> forSet = stringRedisTemplate.opsForSet();

        Object o = forHash.get(SECKILL_KEY, seckillId); //获取秒杀物品
        assert o != null;
        Seckill seckill = JSONArray.parseObject(o.toString(), Seckill.class);// 获取到Seckill对象
        //开始时间判断
        Date beginTime = DateUtil.parseDate(seckill.getBeginTime()); //开始时间
        Date endTime = DateUtil.parseDate(seckill.getEndTime()); //结束时间
        Date date = new Date();
        if (date.before(beginTime)){
            log.info("秒杀未开始");
            return ResultTool.success("秒杀未开始");
        }
        if (date.after(endTime)){
            log.info("秒杀已结束");
            return ResultTool.success("秒杀已结束");
        }
        //判断库存
        int number = Integer.parseInt(forValue.get(SECKILL_KEY_PREFIX + seckillId));
        if (number <= 0){
            log.info("你未抢到");
            return ResultTool.success("没抢上");
        }
        //限制抢购数量
        String token = request.getHeader("token");
        Claims claims = JwtConfig.parseJWT(token);
        String userid = claims.get("id").toString();
        Boolean member = forSet.isMember(SECKILL_KEY_USER + seckillId, userid);
        if (member){
            log.info("限抢一本");
            return ResultTool.success("限量一本");
        }
        //抢购
        //开启Swatch监控
        stringRedisTemplate.watch(SECKILL_KEY_PREFIX + seckillId);
        //事务组块
        SessionCallback<List<String>> stringSessionCallback = new SessionCallback(){

            @Override
            public Object execute(RedisOperations operations) throws DataAccessException {
                // 开启事务
                stringRedisTemplate.multi();
                // 减库存
                forValue.decrement(SECKILL_KEY_PREFIX + seckillId);
                // 加入购买记录
                forSet.add(SECKILL_KEY_USER + seckillId, userid);
                // 执行事务
                return stringRedisTemplate.exec();
            }
        };
        //获取事务处理结果
        List<String> execute = stringRedisTemplate.execute(stringSessionCallback);
        //根据结果执行操作
        if (execute.isEmpty()||execute.get(0)==null){
            return ResultTool.fail(205,"未抢到");
        }
        //抢购成功
        return ResultTool.success("抢购成功");

    }
}
